import datetime
import os.path
import sys
import traceback
from functools import partial

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem, QTreeWidgetItem, QMessageBox, QFrame, QPushButton, \
    QInputDialog, QLineEdit, QAction, QMenu
import qtawesome as qta
from PyQt5.QtCore import QRegExp, Qt
# from pyqtgraph.examples.ExampleApp import PythonHighlighter

# from PyQt5 import uic
from qtpy import uic

from lib.syntaxhighlighter import PythonHighlighter
from share import SI

import glob

# 动态载入
class CodeHelper(QMainWindow):
    def __init__(self):
        super().__init__()
        # PyQt5
        self.ui = uic.loadUi(self.FileUI)
        # 如果使用 self.show(),会产生一个空白的 MainWindow
        self.nodeIcon = qta.icon('ei.file', color='SteelBlue')

        # self.title = '客户端定义'
        self.curTitleBarText = self.Title
        self.curFilePath = None
        self.textChanged = False  # 标记当前文件是否改变
        #用函数拼成的文件斜杠不统一
        self.thisFolderPath = SI.WorkFilePath + '/' + self.Folder
        #文件夹 初始化
        os.makedirs(self.thisFolderPath, exist_ok=True)

        self.ListFileToTree()
        self.InitFrameButton()
        # self.ui.show()   #这块不要先显示出来，要不窗口会有一种闪退的感觉
        #语法 高亮
        self.highlighter = PythonHighlighter(self.ui.textEdit.document())


        #按钮功能
        self.ui.actionNew.triggered.connect(self.ActionAddOne)
        self.ui.actionRefresh.triggered.connect(self.ListFileToTree)
        self.ui.actionSave.triggered.connect(self.ActionSaveFile)
        self.ui.actionRun.triggered.connect(self.ActionRun)
        # 编辑文本框内容改动事件
        self.ui.textEdit.textChanged.connect(self.TextEditChange)

        # 树节点被点击
        self.ui.treeWidgetFile.itemClicked.connect(self.TreeWidgetFileClicked)
        # 设置上下文菜单策略，也可以在 Qt Designer上设置，这块主要是为了添加点击右键删除功能
        self.ui.treeWidgetFile.setContextMenuPolicy(Qt.CustomContextMenu)
        # 定义信号处理方法
        self.ui.treeWidgetFile.customContextMenuRequested.connect(self.ShowMenuOnTreeWidget)
        self.ui.treeWidgetFile.itemChanged.connect(self.TreeItemNameChanged)

    def ActionRun(self):
        pyFile = os.path.join(SI.WorkFilePath, 'run.py')
        # 先确保文件内容保存了
        self.ActionSaveFile()

        code = self.ui.textEdit.toPlainText()

        # 加上开头的一些固定代码行，主要是导入必须的名字
        code = self.TestFileTopLines + code

        with open(pyFile, 'w', encoding='utf8') as f:
            f.write(code)

        cmd = f'cd /d {SI.WorkFilePath} &&start call "{sys.executable}" run.py'
        self.LogInfo(f'产生代码文件 {pyFile} , 运行 ===>')
        os.system(cmd)

    def ActionSaveFile(self):
        if not self.textChanged:
            return

        if not self.curFilePath:
            return

        try:
            with open(self.curFilePath, 'w', encoding='utf8') as f:
                f.write(self.ui.textEdit.toPlainText())

            self.curTitleBarText = f'{self.Title} {os.path.basename(self.curFilePath)}'
            self.ui.setWindowTitle(self.curTitleBarText)
            self.textChanged = False
            self.LogInfo(f'保存文件:{self.curFilePath}')
        except:
            self.LogInfo(traceback.format_exc())

    def TextEditChange(self):
        if self.textChanged:
            return
        self.textChanged = True
        self.ui.setWindowTitle(self.curTitleBarText+' 未保存 ')

    def TreeItemNameChanged(self, item, column):
        newName = item.text(column)
        if newName == item._my_filename:
            return

        filePath = os.path.join(self.thisFolderPath, newName)

        # 文件名冲突，还原
        if os.path.exists(filePath):
            QMessageBox.information(
                self.ui,
                '错误',
                f'文件 {filePath} 已经存在，请重新修改')

            # 原来的名字保存在自己定义的 _my_filename 属性中
            item.setText(0, item._my_filename)
            return

        oriFilePath = os.path.join(self.thisFolderPath, item._my_filename)
        # 修改文件名
        os.rename(oriFilePath, filePath)
        self.LogInfo(f'文件:{item._my_filename}已经重新命名为:{newName}')
        item._my_filename = newName

        self.textChanged = False
        self.curFilePath = filePath
        self.curTitleBarText = f'{self.Title} {os.path.basename(self.curFilePath)}'
        self.ui.setWindowTitle(self.curTitleBarText)
        self.LogInfo(f'保存文件:{self.curFilePath}')


    def ShowMenuOnTreeWidget(self,position):

        tree = self.ui.treeWidgetFile

        # 获取当前用户点选的节点
        curItem = tree.currentItem()

        # 没有当前选中节点
        if not curItem:
            self.LogInfo('没有选中节点，返回')
            return

        # 创建并展示 上下文菜单
        actionDelnode = QAction("删除")
        actionDelnode.triggered.connect(self.ActionDelnode)
        menu = QMenu(tree)
        menu.addAction(actionDelnode)

        menu.exec_(tree.mapToGlobal(position))
    def ActionDelnode(self):
        tree = self.ui.treeWidgetFile

        # 获取当前用户点选的节点
        curItem = tree.currentItem()

        filepath = os.path.join(self.thisFolderPath, curItem.text(0))
        try:
            os.remove(filepath)
            self.LogInfo(f'删除文件:{filepath}')
        except:
            self.LogInfo(f'删除文件:{filepath}遇到异常了\n' + traceback.format_exc())
            pass

        # 找到改节点的父节点
        parentItem = curItem.parent()
        # 如果没有父节点，就是不可见父节点
        if not parentItem:
            parentItem = tree.invisibleRootItem()
        # 删除该节点
        parentItem.removeChild(curItem)

    def ActionAddOne(self):
        # 获得合适的初始名字
        for i in range(1, 10000):
            filename = f'client_{i}.py'
            fileTemp = self.thisFolderPath + '/' + filename
            if not os.path.exists(fileTemp):
                filepath = fileTemp
                break
        open(filepath, 'w', encoding='utf8')

        parentItem = self.ui.treeWidgetFile.invisibleRootItem()

        # 准备一个节点
        nodeitem = QTreeWidgetItem()
        # 设置节点图标
        nodeitem.setIcon(0, self.nodeIcon)
        # 设置该节点  第1个column 文本
        nodeitem.setText(0, filename)
        nodeitem._my_filename = filename  # python 对象可以动态添加属性
        # 设置该节点在以前的flag基础上，多一个可编辑 ItemIsEditable
        nodeitem.setFlags(nodeitem.flags() | Qt.ItemIsEditable)

        # 添加到树的不可见根节点下，就成为第一层节点
        parentItem.addChild(nodeitem)
        parentItem.setExpanded(True)
        self.ui.treeWidgetFile.editItem(nodeitem)
        # while True:
        # filename, okPressed = QInputDialog.getText(self.ui, "请输入文件名字",
        #                                         "文件名",
        #                                         QLineEdit.Normal,
        #                                         filename)
        # filename = filename.strip()
        # if not okPressed :
        #     return
        #
        # filepath = os.path.join(self.thisFolderPath, filename)
        # if os.path.exists(filepath):
        #     QMessageBox.information(
        #         self.ui,
        #         '错误',
        #         f'文件 {filepath} 已经存在，请重新输入')
        # else:
        #     break

    def TreeWidgetFileClicked(self, item, column):
        # 获取被点击的节点文本
        fileName = item.text(column)
        filePath = os.path.join(self.thisFolderPath, fileName)

        # 如果这个要新打开的文件就是当前文件，直接返回
        if filePath == self.curFilePath:
            return

            # 如果前面的当前文件还没有保存，先保存
        if self.textChanged:
            self.ActionSaveFile()

        try:
            with open(filePath, encoding='utf8') as f:
                self.ui.textEdit.setEnabled(True)  # 解除编辑框的禁用状态
                self.ui.textEdit.setPlainText(f.read())

            self.curFilePath = filePath
            self.curTitleBarText = f'{self.Title} {fileName}'
            self.ui.setWindowTitle(self.curTitleBarText)
            self.textChanged = False
        except:
            self.LogInfo(traceback.format_exc())

    def CodeHelperClicked(self, code):

        # 获取发送信号的按钮
        button = self.sender()
        self.LogInfo(f'Button "{button.text()}" clicked')
        if not self.curFilePath:
            QMessageBox.warning(
                self.ui,
                '',
                '请先打开代码文件')
            return
        self.ui.textEdit.insertPlainText(code + '\n')

    def ListFileToTree(self):

        tree = self.ui.treeWidgetFile
        tree.clear()
        # 获取树控件的不可见根节点
        root = tree.invisibleRootItem()

        filePath = self.thisFolderPath + '/*.py'
        self.LogInfo(filePath)

        if os.path.exists(os.path.dirname(filePath)):
            files = glob.glob(filePath)
            for fileOne in files:
                self.LogInfo(fileOne)
        else:
            self.LogInfo(f"Path '{os.path.dirname(filePath)}' does not exist.")
            self.LogInfo('警告', '项目路径未选择, 请确认项目路径')
            return

        idx = 0
        for file in files:
            fname = os.path.basename(file)

            # 准备一个节点
            nodeitem = QTreeWidgetItem()
            # 设置节点图标
            nodeitem.setIcon(0, self.nodeIcon)
            # 设置该节点  第1个column 文本
            nodeitem.setText(0, fname)
            nodeitem._my_filename = fname  # python 对象可以动态添加属性  这块是为了后面改名失败恢复旧名字用的
            # 设置该节点在以前的flag基础上，多一个可编辑 ItemIsEditable
            nodeitem.setFlags(nodeitem.flags() | Qt.ItemIsEditable)
            root.addChild(nodeitem)
    def InitFrameButton(self):
        class QHLine(QFrame):
            def __init__(self):
                super(QHLine, self).__init__()
                self.setFrameShape(QFrame.HLine)
                self.setFrameShadow(QFrame.Sunken)

        for cs in self.CodeSnippets:
            if cs == 'separator':
                self.ui.layout.addWidget(QHLine())
                continue
            btn = QPushButton(cs['name'], self.ui.frame)
            # 定义该按钮的点击处理函数， 这块lambda表达式不行？？？
            # btn.clicked.connect(lambda: self.CodeHelperClicked(cs['code']))
            btn.clicked.connect(partial(self.CodeHelperClicked, cs['code']))
            self.ui.layout.addWidget(btn)
        self.ui.layout.addStretch()  # 这样可以保证里面控件从align top 而不是 居中排列
    def LogInfo(self, info):
        SI.LogInfo(info)
